import RecNPManager from './nativeAudio.js';

export default class AudioStreamRecorder {
    constructor() {
        this.recorderManager = null;
        this.isRecording = false;
        this.platform = this.getPlatform();
        this.audioContext = null;
        this.audioStream = null;
        this.mediaRecorder = null;
        this.processor = null;
        this.onAudioData = null;
        this.mute = false;

        // APP 原生录音插件相关
        // #ifdef APP-PLUS
        // 使用 RecNPManager 统一管理原生插件实例（避免多处 require 冲突）
        // #endif
        this._appJsCallBound = false;
        this.recAliveTimer = null;
        this.recordSampleRate = 48000; // APP原生录音采样率（优先48000，便于2倍下采样）
        this.targetSampleRate = 24000; // 目标采样率，需与后端/播放一致

        this.initRecorder();
    }

    // 获取当前平台
    getPlatform() {
        let platform = '';
        // #ifdef H5
        platform = 'h5';
        // #endif
        // #ifdef MP-WEIXIN
        platform = 'mp-weixin';
        // #endif
        // #ifdef APP-PLUS
        platform = 'app';
        // #endif
        return platform;
    }

    initRecorder() {
        if (this.platform === 'h5') {
            this.initH5Recorder();
        } else if (this.platform === 'mp-weixin') {
            this.initMiniProgramRecorder();
        } else if (this.platform === 'app') {
            this.initAppRecorder();
        }
    }

    // 初始化H5录音器
    initH5Recorder() {
        console.log('初始化H5音频流录音器');
    }

    // 初始化小程序录音器
    initMiniProgramRecorder() {
        console.log('初始化小程序音频流录音器');
        this.recorderManager = uni.getRecorderManager();

        // 监听录音开始事件
        this.recorderManager.onStart(() => {
            console.log('小程序录音开始');
            this.isRecording = true;
        });

        // 监听录音停止事件
        this.recorderManager.onStop(res => {
            console.log('小程序录音停止', res);
            this.isRecording = false;
        });

        // 监听录音错误事件
        this.recorderManager.onError(err => {
            console.error('小程序录音错误', err);
            this.isRecording = false;
            uni.showToast({
                title: '录音失败：' + err.errMsg,
                icon: 'none',
                duration: 3000,
            });
        });

        // 监听录音过程中的音频数据
        this.recorderManager.onFrameRecorded(res => {
            if (this.onAudioData && !this.mute) {
                // 将音频数据转换为base64格式
                const base64 = this.arrayBufferToBase64(res.frameBuffer);
                this.onAudioData(base64);
            }
        });
    }

    // 初始化APP原生录音
    initAppRecorder() {
        // #ifdef APP-PLUS
        if (this._appJsCallBound) return;
        RecNPManager.bindJsCallOnce();
        this._onRecordHandler = data => {
            if (!data || data.action !== 'onRecord') return;
            if (!this.onAudioData || this.mute) return;
            try {
                const ab = uni.base64ToArrayBuffer(data.pcmDataBase64);
                let int16 = new Int16Array(ab);
                if (
                    Number(data.sampleRate) !== this.targetSampleRate &&
                    Number(data.sampleRate) === 48000 &&
                    this.targetSampleRate === 24000
                ) {
                    int16 = this.downsampleInt16(int16, 2);
                }
                const outBase64 = this.arrayBufferToBase64(int16.buffer);
                this.onAudioData(outBase64);
            } catch (e) {
                console.error('APP录音数据处理失败:', e);
            }
        };
        this._onRecErrorHandler = data => {
            if (!data || data.action !== 'onRecError') return;
            console.error('APP录音错误:', data.message);
            this.isRecording = false;
        };
        this._onInterruptionBeginHandler = () => {
            this.isRecording = false;
        };
        this._onInterruptionEndHandler = () => {};
        RecNPManager.on('onRecord', this._onRecordHandler);
        RecNPManager.on('onRecError', this._onRecErrorHandler);
        RecNPManager.on('onInterruptionBegin', this._onInterruptionBeginHandler);
        RecNPManager.on('onInterruptionEnd', this._onInterruptionEndHandler);
        this._appJsCallBound = true;
        // #endif
    }

    // 开始录音
    async startRecording(onAudioDataCallback) {
        this.onAudioData = onAudioDataCallback;

        if (this.platform === 'h5') {
            return this.startH5Recording();
        } else if (this.platform === 'mp-weixin') {
            return this.startMiniProgramRecording();
        } else if (this.platform === 'app') {
            return this.startAppRecording();
        }
    }

    // H5录音开始
    async startH5Recording() {
        try {
            // 配置音频约束，启用回声消除和降噪
            const audioConstraints = {
                audio: {
                    echoCancellation: true, // 回声消除
                    noiseSuppression: true, // 噪声抑制
                    autoGainControl: true, // 自动增益控制
                    sampleRate: 24000, // 采样率
                    channelCount: 1, // 单声道
                    // 避免录制系统音频
                    googEchoCancellation: true,
                    googAutoGainControl: true,
                    googNoiseSuppression: true,
                    googHighpassFilter: true,
                    googTypingNoiseDetection: true,
                    googAudioMirroring: false, // 禁用音频镜像
                },
            };

            this.audioStream = await navigator.mediaDevices.getUserMedia(audioConstraints);
            this.audioContext = new AudioContext({
                sampleRate: 24000, // 采样率，与小程序保持一致
                channelCount: 1,
            });
            const source = this.audioContext.createMediaStreamSource(this.audioStream);
            this.processor = this.audioContext.createScriptProcessor(4096, 1, 1);
            source.connect(this.processor);
            this.processor.connect(this.audioContext.destination);

            this.processor.onaudioprocess = e => {
                if (this.mute) return; // 静音时不发送音频数据
                const inputData = e.inputBuffer.getChannelData(0);
                const pcmData = new Int16Array(inputData.length);
                for (let i = 0; i < inputData.length; i++) {
                    pcmData[i] = inputData[i] * 0x7fff;
                }
                const buffer = pcmData.buffer;
                const base64 = this.arrayBufferToBase64(buffer);
                if (this.onAudioData) {
                    this.onAudioData(base64);
                }
            };

            this.isRecording = true;
        } catch (error) {
            console.error('H5录音启动失败:', error);
            uni.showToast({
                title: '录音启动失败',
                icon: 'none',
                duration: 2000,
            });
            throw error;
        }
    }

    // 小程序录音开始
    async startMiniProgramRecording() {
        // 检查录音权限
        const authResult = await this.checkRecordAuth();
        if (!authResult) {
            throw new Error('录音权限被拒绝');
        }

        // 开始录音
        this.recorderManager.start({
            sampleRate: 24000, // 采样率，微信小程序推荐24000
            numberOfChannels: 1, // 录音通道数
            format: 'pcm', // 音频格式，使用pcm以便实时处理
            frameSize: 2, // 指定帧大小
            // 微信小程序特有的音频处理参数
        });
    }

    // APP原生录音开始
    async startAppRecording() {
        // #ifdef APP-PLUS
        try {
            const hasPerm = await this.checkRecordAuth();
            if (!hasPerm) throw new Error('录音权限被拒绝');

            // 建议设置为听筒播放以减少回声；录音时启用回声消除
            await this.RecNP_CallAsync('recordStart', {
                appNativePlugin_sampleRate: this.recordSampleRate, // 实测可用48000
                appNativePlugin_AEC_Enable: true,
                // 使用扬声器播放（外放），避免走听筒
                setSpeakerOff: { off: false, headset: false },
            });

            // 心跳保活：每5秒一次
            this.clearRecordAlive();
            this.recAliveTimer = setInterval(() => {
                this.RecNP_CallAsync('recordAlive', {}).catch(() => {});
            }, 5000);

            this.isRecording = true;
        } catch (e) {
            console.error('APP录音启动失败:', e);
            this.isRecording = false;
            throw e;
        }
        // #endif
    }

    // 停止录音
    stopRecording() {
        if (this.platform === 'h5') {
            if (this.processor) {
                this.processor.disconnect();
                this.processor = null;
            }
            if (this.audioContext) {
                this.audioContext.close();
                this.audioContext = null;
            }
            if (this.audioStream) {
                this.audioStream.getTracks().forEach(track => track.stop());
                this.audioStream = null;
            }
            this.isRecording = false;
        } else if (this.platform === 'mp-weixin') {
            if (this.recorderManager && this.isRecording) {
                this.recorderManager.stop();
            }
        } else if (this.platform === 'app') {
            this.stopAppRecording();
        }
    }

    // APP原生录音停止
    stopAppRecording() {
        // #ifdef APP-PLUS
        this.clearRecordAlive();
        this.RecNP_CallAsync('recordStop', {})
            .catch(err => console.error('APP停止录音失败:', err))
            .finally(() => {
                this.isRecording = false;
            });
        // #endif
    }

    // 设置静音状态（静音时仍保持录音但不回调数据）
    setMute(mute) {
        this.mute = mute;
    }

    // 切换扬声器/听筒（仅APP）
    async setSpeakerMode({ off = true, headset = true } = {}) {
        // #ifdef APP-PLUS
        try {
            await this.RecNP_CallAsync('setSpeakerOff', { off, headset });
        } catch (e) {
            console.error('切换扬声器失败:', e);
        }
        // #endif
    }

    // 检查录音权限
    async checkRecordAuth() {
        if (this.platform === 'h5') {
            // H5平台权限检查在startH5Recording中处理
            return true;
        }
        if (this.platform === 'mp-weixin') {
            // 小程序权限检查
            return new Promise(resolve => {
                uni.authorize({
                    scope: 'scope.record',
                    success() {
                        resolve(true);
                    },
                    fail() {
                        uni.showModal({
                            title: '授权提示',
                            content: '需要录音权限才能使用语音功能',
                            showCancel: false,
                            confirmText: '去设置',
                            success: res => {
                                if (res.confirm) {
                                    uni.openSetting({
                                        success: settingRes => {
                                            if (settingRes.authSetting['scope.record']) {
                                                resolve(true);
                                            } else {
                                                resolve(false);
                                            }
                                        },
                                        fail: () => {
                                            resolve(false);
                                        },
                                    });
                                } else {
                                    resolve(false);
                                }
                            },
                        });
                    },
                });
            });
        }
        // APP 原生权限
        // #ifdef APP-PLUS
        try {
            const code = await this.RecNP_CallAsync('recordPermission', {});
            return Number(code) === 1;
        } catch (e) {
            console.error('APP录音权限检查失败:', e);
            return false;
        }
        // #endif
    }

    // ArrayBuffer转Base64
    arrayBufferToBase64(buffer) {
        const bytes = new Uint8Array(buffer);
        let binary = '';
        for (let i = 0; i < bytes.byteLength; i++) {
            binary += String.fromCharCode(bytes[i]);
        }

        // 检查是否有btoa函数，如果没有则使用自定义实现
        if (typeof btoa !== 'undefined') {
            return btoa(binary);
        } else {
            // 自定义base64编码实现
            return this.customBtoa(binary);
        }
    }

    // 自定义base64编码函数，兼容微信小程序
    customBtoa(str) {
        const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
        let output = '';
        let i = 0;

        while (i < str.length) {
            const chr1 = str.charCodeAt(i++);
            const chr2 = str.charCodeAt(i++);
            const chr3 = str.charCodeAt(i++);

            const enc1 = chr1 >> 2;
            const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            let enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            let enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output =
                output +
                chars.charAt(enc1) +
                chars.charAt(enc2) +
                chars.charAt(enc3) +
                chars.charAt(enc4);
        }

        return output;
    }

    // 简单整数倍降采样（例如 48000->24000），输入Int16
    downsampleInt16(int16Array, factor) {
        if (factor <= 1) return int16Array;
        const newLen = Math.floor(int16Array.length / factor);
        const result = new Int16Array(newLen);
        for (let i = 0, j = 0; j < newLen; i += factor, j++) {
            result[j] = int16Array[i];
        }
        return result;
    }

    // 统一的原生调用Promise封装（APP）
    RecNP_CallAsync(action, args) {
        // #ifdef APP-PLUS
        return RecNPManager.call(action, args || {});
        // #endif
    }

    clearRecordAlive() {
        if (this.recAliveTimer) {
            clearInterval(this.recAliveTimer);
            this.recAliveTimer = null;
        }
    }

    // 销毁录音器
    destroy() {
        this.stopRecording();
        this.onAudioData = null;
        this.clearRecordAlive();
        // #ifdef APP-PLUS
        if (this._appJsCallBound) {
            if (this._onRecordHandler) RecNPManager.off('onRecord', this._onRecordHandler);
            if (this._onRecErrorHandler) RecNPManager.off('onRecError', this._onRecErrorHandler);
            if (this._onInterruptionBeginHandler)
                RecNPManager.off('onInterruptionBegin', this._onInterruptionBeginHandler);
            if (this._onInterruptionEndHandler)
                RecNPManager.off('onInterruptionEnd', this._onInterruptionEndHandler);
            this._appJsCallBound = false;
        }
        // #endif
    }
}
